/*:
* @plugindesc (v1.05)单挑
* @author 小c
* @version 1.0.5
* @date 12/1/2020
*
* @param Start Solo Function
* @text 战斗中启动单挑额外代码
* @type note
* @default "//本函数在战斗中启动单挑时触发，可用于处理UI变更\nBattleManager.refreshStatus();"
*
* @param End Solo Function
* @text 战斗中结束单挑额外代码
* @type note
* @default "//本函数在战斗中结束单挑时触发，可用于处理UI变更\nBattleManager.refreshStatus();"
*
* @help
* 其他人不离队的情况下启动单挑模式
* 注意：如果你使用的插件有战斗中切换场景的效果，请对函数
* BattleManager.canRemoveSoloOnBattleEnd进行修正。
* 目前已经完成了对YEP_PartySystem的修正工作。
*
* 敌群事件注释
* 在敌群事件的第一页加入以下注释
* <SoloBattle: Actor x> 以角色x进入单挑模式
* <SoloBattle: Party x> 以队伍序号x进入单挑模式，x最小值为1
* 此敌群为单挑战斗
* 使用注释标签进入单挑，单挑结束将自动退出单挑模式
* 
* 插件指令
* StartSoloBattle Actor x 设置单挑成员，进入单挑模式
* 其中x为actorId，此角色必须在队列中【在后队也生效】
* StartSoloBattle Party x 设置单挑成员，进入单挑模式
* 其中x为角色在队伍中的序号，最小值为1
* EndSoloBattle 结束单挑模式，队列恢复
* 两个插件指令可在战斗中使用
* 当你在战斗中调用他们的时候，会执行对应的插件参数函数
* 这两个参数用于处理战斗角色变更时的附属操作，比如UI的变换
* 这两个函数的this指向$gameParty
* 一般情况下请不要改变这两个函数的默认值内容，添加你自己的内容即可。
* 默认内容为刷新战斗状态窗口，是非常必要的，
*
* 使用插件指令进入单挑模式后，一般情况下必须通过插件指令解除单挑模式
* 在插件指令进入单挑模式时，若遇到具有单挑模式的敌群战斗，单挑模式将
* 被修改为敌群设置
* 敌群设置的单挑结束，将自动退出单挑模式
*
* 敌群事件注释
* 在敌群事件第一页有以下标签
* <Battle End Remove Solo>
* 该标签表示本场战斗结束后强制退出单挑模式
* 可用于战斗中途进入单挑模式后，战斗结束解除的情况
*
* 更新日志
* v1.00
* 插件完成
*
* v1.01
* 追加指定partyIndex进入单挑
* 追加事件注释指定敌群单挑
*
* v1.02
* 更新战斗中触发单挑时状态窗口的刷新机制
*
* v1.03
* 增加战斗中操作单挑的额外代码接口
*
* v1.04
* 解决单挑结束时状态窗口可能的闪屏问题
* 允许战斗结束强制解除单挑
*
* v1.05
* 解决当单挑角色死亡时触发单挑直接判断为我方全灭的问题
* 此时单挑者以1血进入战斗
*/

var Imported = Imported || {};
Imported.CP_SoloManager = true;

var CP = CP || {};
CP.SoloManager = CP.SoloManager || {};
CP.SoloManager.Params = PluginManager.parameters("CP_SoloManager");
CP.SoloManager.START_SOLO_FUNCTION_IN_BATTLE = new Function(JSON.parse(CP.SoloManager
	.Params["Start Solo Function"]));
CP.SoloManager.END_SOLO_FUNCTION_IN_BATTLE = new Function(JSON.parse(CP.SoloManager
	.Params["End Solo Function"]));

CP.SoloManager._loaded = false;
CP.SoloManager.DATABASE_LOADED = DataManager.isDatabaseLoaded;
DataManager.isDatabaseLoaded = function(){
	if(!CP.SoloManager.DATABASE_LOADED.call(this))
		return false;

	if(!CP.SoloManager._loaded){
		CP.SoloManager.loadTroopSolo();
		CP.SoloManager._loaded = true;
	}

	return true;
};

CP.SoloManager.loadTroopSolo = function(){
	var regA = /<SoloBattle: Actor (\d+)>/; //角色单挑标签
	var regP = /<SoloBattle: Party (\d+)>/; //团队单挑标签
	var regE = /<Battle End Remove Solo>/; //战斗结束自动解除单挑

	for(var i = 1; i < $dataTroops.length; i++){
		var troop = $dataTroops[i];
		troop.soloActor = 0;
		troop.soloParty = 0;
		troop.endBattleRemoveSolo = false;

		var list = troop.pages[0].list;
		list.forEach(function(ev){
			if([108, 408].contains(ev.code)){
				if(regE.exec(ev.parameters[0]))
					troop.endBattleRemoveSolo = true;
				if(regA.exec(ev.parameters[0]))
					troop.soloActor = Number(RegExp.$1) || 0;
				else if(regP.exec(ev.parameters[0]))
					troop.soloParty = Number(RegExp.$1) || 0;
			}
		});
	}
};

//----------------------------
//  Game_Party
//----------------------------
CP.SoloManager.INIT_PARTY = Game_Party.prototype.initialize;
Game_Party.prototype.initialize = function(){
	CP.SoloManager.INIT_PARTY.call(this);

	this.removeSoloActor();
};

Game_Party.prototype.setSoloActor = function(actorId){
	this._soloActorId = actorId;
};

Game_Party.prototype.soloActor = function(){
	return $gameActors.actor(this._soloActorId);
};

Game_Party.prototype.startSoloBattle = function(actorId){
	var actor = $gameActors.actor(actorId);
	if(!!actor && actor.isInParty()){
		//添加单挑时，复活单挑成员
		this.setSoloActor(actorId);
		this.reviveSoloActor();
		if(this.inBattle())
			CP.SoloManager.START_SOLO_FUNCTION_IN_BATTLE.call(this);
		return true;
	}else{
		this.removeSoloActor();
		return false;
	}
};

Game_Party.prototype.removeSoloActor = function(){
	this.setSoloActor(0);
};

Game_Party.prototype.endSoloBattle = function(){
	this.removeSoloActor();
	if(this.inBattle())
		CP.SoloManager.END_SOLO_FUNCTION_IN_BATTLE.call(this);
	$gamePlayer.refresh();
};

Game_Party.prototype.isSoloBattle = function(){
	return this._soloActorId > 0;
};

//复苏单挑人员
Game_Party.prototype.reviveSoloActor = function(){
	var actor = $gameParty.soloActor();
	//添加单挑时，复活单挑成员
	if(!!actor && actor.isDead())
		actor.removeState(actor.deathStateId());
};

CP.SoloManager.BATTLE_MEMBERS = Game_Party.prototype.battleMembers;
Game_Party.prototype.battleMembers = function() {
    if(this.isSoloBattle())
    	return [this.soloActor()];

    return CP.SoloManager.BATTLE_MEMBERS.call(this);
};

CP.SoloManager.PARTY_ALL_DEAD = Game_Party.prototype.isAllDead;
Game_Party.prototype.isAllDead = function(){
	if(this.isSoloBattle() && this.soloActor().isDead())
		return true;

	return CP.SoloManager.PARTY_ALL_DEAD.call(this);
};

//----------------------------
//  Game_Actor
//----------------------------
Game_Actor.prototype.isSoloBattler = function(){
	if(!$gameParty.isSoloBattle())
		return false;

	return this === $gameParty.soloActor();
};

CP.SoloManager.ACTOR_FINAL_EXP_RATE = Game_Actor.prototype.finalExpRate;
Game_Actor.prototype.finalExpRate = function() {
    if($gameParty.isSoloBattle() && !this.isSoloBattler())
    	return 0;
    else
    	return CP.SoloManager.ACTOR_FINAL_EXP_RATE.call(this);
};

Game_Actor.prototype.isInParty = function(){
	return $gameParty.allMembers().contains(this);
};

//----------------------------
//  Game_Troop
//----------------------------
Game_Troop.prototype.soloActorId = function(){
	return this.troop().soloActor;
};

Game_Troop.prototype.soloPartyId = function(){
	return this.troop().soloParty;
};

Game_Troop.prototype.isEndBattleRemoveSolo = function(){
	return this.troop().endBattleRemoveSolo;
};

Game_Troop.prototype.getSoloActorId = function(){
	var a = this.soloActorId();
	var p = this.soloPartyId();

	if(a > 0)
		return a;
	else if(p > 0){
		var member = $gameParty.allMembers()[p - 1];
		if(!!member)
			return member.actorId();
		else
			return 0;
	}else
		return 0;
};

//----------------------------
//  Game_Interpreter
//----------------------------
CP.SoloManager.PLUGIN_COMMAND = Game_Interpreter.prototype.pluginCommand;
Game_Interpreter.prototype.pluginCommand = function(command, args){
	CP.SoloManager.PLUGIN_COMMAND.call(this, command, args);

	if(command == 'StartSoloBattle'){
		var actorId = 0;
		var type = args[0].trim().toUpperCase();
		var value = Number(args[1]) || 0;
		if(type == "ACTOR")
			actorId = value;
		else if(type == "PARTY"){
			var member = $gameParty.allMembers()[value - 1];
			if(!!member)
				actorId = member.actorId();
			else
				actorId = 0;
		}

		$gameParty.startSoloBattle(actorId);
	}

	if(command == 'EndSoloBattle')
		$gameParty.endSoloBattle();
};

//----------------------------
//  Sprite_Actor
//----------------------------
CP.SoloManager.SET_ACTOR_SPRITE_HOME = Sprite_Actor.prototype.setActorHome;
Sprite_Actor.prototype.setActorHome = function(index){
	if($gameParty.isSoloBattle())
		index = 0;

	CP.SoloManager.SET_ACTOR_SPRITE_HOME.call(this, index);
};

//----------------------------
//  BattleManager
//----------------------------
CP.SoloManager.SETUP_BATTLE = BattleManager.setup;
BattleManager.setup = function(troopId, canEscape, canLose){
	CP.SoloManager.SETUP_BATTLE.call(this, troopId, canEscape, canLose);

	this.setupSolo();
};

BattleManager.setupSolo = function(){
	var soloActor = $gameTroop.getSoloActorId();
	if(soloActor > 0)	
		this._soloTroop = $gameParty.startSoloBattle(soloActor);
	else
		this._soloTroop = false;
	this.onSetupSolo();
};

//初始化单体执行效果
//当存在死亡单挑对象时，复活。
BattleManager.onSetupSolo = function(){
	$gameParty.reviveSoloActor();
};

//本场战斗在敌群层面是否为单挑
//即本敌群有没有单挑标签
BattleManager.isCurrentBattleSolo = function(){
	return !!this._soloTroop;
};

//本场战斗是否结束解除单挑
BattleManager.isEndBattleRemoveSolo = function(){
	return $gameTroop.isEndBattleRemoveSolo();
};

//战斗结束的解除单挑条件：适配YEP队伍核心
BattleManager.canRemoveSoloOnBattleEnd = function(){
	if(Imported.YEP_PartySystem && $gameTemp._partyBattle)
		return false;
	else
		return this.isCurrentBattleSolo() || this.isEndBattleRemoveSolo();
};

//----------------------------
//  Scene_Battle
//----------------------------
//真·战斗结束时点：战斗场景销毁
//此时才解除单挑状态不会因为人物变更引起的闪烁
CP.SoloManager.TERMINATE_SCENE_BATTLE = Scene_Battle.prototype.terminate;
Scene_Battle.prototype.terminate = function(){
    CP.SoloManager.TERMINATE_SCENE_BATTLE.call(this);
    if(BattleManager.canRemoveSoloOnBattleEnd())
    	$gameParty.endSoloBattle();
};